﻿using LCFCBase;
using NPOI.SS.Formula.Functions;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;

namespace TYBase
{

    public class TYHttp
    {

        public string BaseUri { get; set; }
        public string Url { get; set; }

        public PostDataType PostDataType { get; set; } = PostDataType.String;

        public EnumHttpMethod HttpMethod { get; set; } = EnumHttpMethod.Get;

        public byte[] Data { get; set; }
        public string DataStr { get; private set; }
        public string ContentType { get; set; } = "application/json";

        public Dictionary<string, string> Headers { get; set; }

        public TYHttp Addheader(string key, string value)
        {
            if (Headers == null)
                Headers = new Dictionary<string, string>();
            Headers[key] = value;
            return this;
        }

        public int OutTime { get; private set; } = 5000;
        public string Prefix { get; private set; }
        public string serviceUrl { get; private set; }
        public string QueryUrl { get; private set; }

        public TYHttp SetQueryUrl(string queryUrl)
        {
            QueryUrl = queryUrl;
            return this;
        }

        public TYHttp SetData(string data)
        {
            Data = Encoding.UTF8.GetBytes(data);
            DataStr = data;
            PostDataType = PostDataType.String;
            return this;
        }

        public TYHttp SetData(byte[] data)
        {
            Data = data;
            PostDataType = PostDataType.Byte;
            return this;
        }
        public TYHttp SetBaseUri(string uri)
        {
            BaseUri = uri;
            return this;
        }


        public TYHttp SetUrl(string uri)
        {
            Url = uri;
            return this;
        }
        public TYHttp SetOutTime(int milSecond)
        {
            OutTime = milSecond;
            return this;
        }
        public TYHttp SetGet()
        {
            HttpMethod = EnumHttpMethod.Get;
            return this;
        }

        public TYHttp SetPost()
        {
            HttpMethod = EnumHttpMethod.Post;
            return this;
        }
        public TYHttp SetPut()
        {
            HttpMethod = EnumHttpMethod.Put;
            return this;
        }
        public TYHttp SetDelete()
        {
            HttpMethod = EnumHttpMethod.Delete;
            return this;
        }
        public TYHttp SetHead()
        {
            HttpMethod = EnumHttpMethod.Head
                ;
            return this;
        }
        public TYHttp SetPrefix(string profix)
        {
            Prefix = profix;
            return this;
        }


        public virtual bool Excute(out string result)
        {
            result = "";
            //先根据用户请求的uri构造请求地址
            string subUrl = "";
            if (Prefix.Null())
                subUrl = $"/{Url}";
            else
            {
                subUrl = $"/{Prefix}/{Url}";
            }


            subUrl = subUrl.Replace("//", "/");
            serviceUrl = $"{this.BaseUri}{subUrl}";


            if (this.BaseUri.Null())
            {
                serviceUrl = Url;
            }

            if (QueryUrl.NotNull())
            {
                serviceUrl = $"{serviceUrl}?{QueryUrl}";
            }

            HttpBase http = new HttpBase();
            HttpItem httpItem = new HttpItem()
            {
                URL = serviceUrl,
                Postdata = DataStr,
                Method = $"{HttpMethod}".ToUpper(),
                PostDataType = PostDataType.String,
                Encoding = Encoding.UTF8,
                ReadWriteTimeout = OutTime,
                Timeout = OutTime,
                ContentType = ContentType,

            };
            if (PostDataType == PostDataType.Byte)
            {
                httpItem.PostdataByte = Data;
                httpItem.PostDataType = PostDataType.Byte;
            }
            if (Headers != null)
            {
                httpItem.Header = new WebHeaderCollection();
                foreach (var item in Headers)
                {
                    httpItem.Header.Add(item.Key, item.Value);
                }
            }

            var ret = http.GetHtml(httpItem);
            result = ret?.Html;
            Log.I($"url({HttpMethod}):{serviceUrl}");
            if (DataStr.NotNull())
            {
                Log.I($"data:{DataStr}");
            }
            Log.I($"res({ret.Html})");
            if (ret.StatusCode == HttpStatusCode.OK)
            {
                return true;
            }
            result = $"[{(int)ret.StatusCode}] {result}";
            //System.Uri uri = new Uri(serviceUrl);
            return false;
        }

        public TYHttp SetBaseUri(string url, int port)
        {
            if (url.Null())
            {
                BaseUri = "";
            }
            else
            {
                if (port > 0)
                {
                    BaseUri = $"http://{url}:{port}";
                }
                else
                {
                    BaseUri = $"http://{url}";
                }
            }

            return this;

        }
        private bool UploadFilesToServer(string uri, string fileName, Dictionary<string, string> data, out string result)
        {
            try
            {


                string boundary = "----------" + DateTime.Now.Ticks.ToString("x");
                HttpWebRequest httpReq = (HttpWebRequest)WebRequest.Create(new Uri(uri));
                httpReq.ContentType = "multipart/form-data; boundary=" + boundary;
                httpReq.Method = "POST";
                //httpReq.AllowWriteStreamBuffering = false; //对发送的数据不使用缓存
                httpReq.Timeout = 300000;  //设置获得响应的超时时间（300秒）

                using (Stream postStream = httpReq.GetRequestStream())
                {
                    WriteMultipartForm(postStream, data, boundary, fileName);
                }
                using (HttpWebResponse response = (HttpWebResponse)httpReq.GetResponse())
                {
                    Stream receiveStream = response.GetResponseStream();
                    StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8);
                    result = readStream.ReadToEnd();

                    response.Close();
                    readStream.Close();
                }
                return true;
            }
            catch (Exception ex)
            {
                result = ex.Info();
                return false;
            }
        }
        private bool WriteMultipartForm(Stream s,
            Dictionary<string, string> data,
            string boundary,
            string filePath)
        {
            if (!File.Exists(filePath))
                return false;
            string fileName = Path.GetFileNameWithoutExtension(filePath);
            string fileContentType = Path.GetExtension(filePath);
            /// The first boundary
            byte[] boundarybytes = Encoding.UTF8.GetBytes("--" + boundary + "\r\n");
            /// the last boundary.
            byte[] trailer = Encoding.UTF8.GetBytes("\r\n--" + boundary + "--\r\n");
            /// the form data, properly formatted
            string formdataTemplate = "Content-Dis-data; name=\"{0}\"\r\n\r\n{1}";
            /// the form-data file upload, properly formatted
            string fileheaderTemplate = "Content-Dis-data; name=\"{0}\"; filename=\"{1}\";\r\nContent-Type: {2}\r\n\r\n";

            /// Added to track if we need a CRLF or not.

            bool bNeedsCRLF = false;

            if (data != null)
            {
                foreach (string key in data.Keys)
                {
                    /// if we need to drop a CRLF, do that.
                    if (bNeedsCRLF)
                        WriteToStream(s, "\r\n");

                    /// Write the boundary.
                    WriteToStream(s, boundarybytes);

                    /// Write the key.
                    WriteToStream(s, string.Format(formdataTemplate, key, data[key]));
                    bNeedsCRLF = true;
                }
            }

            /// If we don't have keys, we don't need a crlf.
            if (bNeedsCRLF)
                WriteToStream(s, "\r\n");

            WriteToStream(s, boundarybytes);
            WriteToStream(s, string.Format(fileheaderTemplate, "file", fileName, fileContentType));
            /// Write the file data to the stream.
            FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            BinaryReader binaryReader = new BinaryReader(fileStream);

            int bufferLength = 4096;
            byte[] buffer = new byte[bufferLength];

            //已上传的字节数
            long offset = 0;
            int size = binaryReader.Read(buffer, 0, bufferLength);
            while (size > 0)
            {
                WriteToStream(s, buffer);
                offset += size;
                size = binaryReader.Read(buffer, 0, bufferLength);
            }
            WriteToStream(s, trailer);
            return true;
        }

        /// <summary>
        /// Writes string to stream. Author : Farhan Ghumra
        /// </summary>
        private void WriteToStream(Stream s, string txt)
        {
            byte[] bytes = Encoding.UTF8.GetBytes(txt);
            s.Write(bytes, 0, bytes.Length);
        }

        /// <summary>
        /// Writes byte array to stream. Author : Farhan Ghumra
        /// </summary>
        private void WriteToStream(Stream s, byte[] bytes)
        {
            s.Write(bytes, 0, bytes.Length);
        }

        public virtual bool UploadFile(string filePath, out string result)
        {
            result = "";
            //先根据用户请求的uri构造请求地址
            string subUrl = "";
            if (Prefix.Null())
                subUrl = $"/{Url}";
            else
            {
                subUrl = $"/{Prefix}/{Url}";
            }

            subUrl = subUrl.Replace("//", "/");
            serviceUrl = $"{this.BaseUri}{subUrl}";
            if (this.BaseUri.Null())
            {
                serviceUrl = Url;
            }
            try
            {
                // 时间戳，用做boundary
                string timeStamp = DateTime.Now.Ticks.ToString("x");

                //根据uri创建HttpWebRequest对象
                HttpWebRequest httpReq = (HttpWebRequest)WebRequest.Create(new Uri(serviceUrl));
                httpReq.Method = "POST";
                httpReq.AllowWriteStreamBuffering = false; //对发送的数据不使用缓存
                httpReq.Timeout = 300000;  //设置获得响应的超时时间（300秒）
                httpReq.ContentType = "multipart/form-data; boundary=" + timeStamp;

                if (Headers != null)
                {
                    foreach (var item in Headers)
                    {
                        httpReq.Headers.Add(item.Key, item.Value);
                    }
                }

                //文件
                FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
                BinaryReader binaryReader = new BinaryReader(fileStream);

                //头信息
                string boundary = "--" + timeStamp;
                string dataFormat = boundary + "\r\nContent-Disposition: form-data; name=\"{0}\";filename=\"{1}\"\r\nContent-Type:application/octet-stream\r\n\r\n";
                string header = string.Format(dataFormat, "file", Path.GetFileName(filePath));
                byte[] postHeaderBytes = Encoding.UTF8.GetBytes(header);

                //结束边界
                byte[] boundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + timeStamp + "--\r\n");

                long length = fileStream.Length + postHeaderBytes.Length + boundaryBytes.Length;

                httpReq.ContentLength = length;//请求内容长度

                try
                {
                    //每次上传4k
                    int bufferLength = 4096;
                    byte[] buffer = new byte[bufferLength];

                    //已上传的字节数
                    long offset = 0;
                    int size = binaryReader.Read(buffer, 0, bufferLength);
                    Stream postStream = httpReq.GetRequestStream();

                    //发送请求头部消息
                    postStream.Write(postHeaderBytes, 0, postHeaderBytes.Length);

                    while (size > 0)
                    {
                        postStream.Write(buffer, 0, size);
                        offset += size;
                        size = binaryReader.Read(buffer, 0, bufferLength);
                    }

                    //添加尾部边界
                    postStream.Write(boundaryBytes, 0, boundaryBytes.Length);
                    postStream.Close();

                    //获取服务器端的响应
                    using (HttpWebResponse response = (HttpWebResponse)httpReq.GetResponse())
                    {
                        Stream receiveStream = response.GetResponseStream();
                        StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8);
                        result = readStream.ReadToEnd();

                        response.Close();
                        readStream.Close();
                    }
                    return true;
                }
                catch (Exception ex)
                {
                    result = ex.Info();
                }
                finally
                {
                    fileStream.Close();
                    binaryReader.Close();
                }
            }
            catch (Exception ex2)
            {
                result = ex2.Info();
            }
            return false;
        }


        public bool UploadFile(string filePath, Dictionary<string, string> dics, out string result)
        {
            result = "";
            //先根据用户请求的uri构造请求地址
            string subUrl = "";
            if (Prefix.Null())
                subUrl = $"/{Url}";
            else
            {
                subUrl = $"/{Prefix}/{Url}";
            }

            subUrl = subUrl.Replace("//", "/");
            serviceUrl = $"{this.BaseUri}{subUrl}";
            if (this.BaseUri.Null())
            {
                serviceUrl = Url;
            }
            return UploadFilesToServer(serviceUrl, filePath, dics, out result);

        }

    }

    public enum EnumHttpMethod
    {
        Get,
        Post,
        Put,
        Delete,
        Head,
    }
}
